package com.kayang.common;

import com.kayang.nadapt.util.JsonConvert;
import com.kayang.nadapt.util.LogHelper;
import com.kayang.nadapt.util.StringUtil;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.vavr.control.Try;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

public class HttpResilience4jCommand {

    private String url;
    private String method;
    private Map<String, Object> param;
    private Map<String, String> header;
    private RestTemplate restTemplate = new RestTemplate();

    private CircuitBreaker circuitBreaker;

    public HttpResilience4jCommand(String url, String method) {
        this(url, method, null, null);
    }

    public HttpResilience4jCommand(String url, String method, Map<String, Object> param) {
        this(url, method, param, null);
    }

    public HttpResilience4jCommand(String url, String method, Map<String, Object> param, Map<String, String> header) {
        this.url = url;
        this.method = method;
        this.param = param;
        this.header = header;

        // 创建 CircuitBreaker 配置
        CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
                .slidingWindowSize(10)
                .minimumNumberOfCalls(5)
                .failureRateThreshold(50)
                .waitDurationInOpenState(java.time.Duration.ofSeconds(10))
                .permittedNumberOfCallsInHalfOpenState(3)
                .automaticTransitionFromOpenToHalfOpenEnabled(true)
                .build();

        // 创建 CircuitBreakerRegistry
        CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig);

        // 获取 CircuitBreaker 实例
        this.circuitBreaker = circuitBreakerRegistry.circuitBreaker("HttpResilience4jCommand");
    }

    public Map execute() {
        // 使用 decorateSupplier 装饰 Supplier 函数，并提供回退函数
        Try<Map> result = Try.ofSupplier(circuitBreaker.decorateSupplier(() -> {
            method = "q";
            Map ret;
            // 业务逻辑
            if ("".equals(method) || method == null || "get".equalsIgnoreCase(method)) {
                ret = get();
            } else if ("patch".equalsIgnoreCase(method)) {
                ret = patch();
            } else if ("put".equalsIgnoreCase(method)) {
                ret = put();
            } else if ("delete".equalsIgnoreCase(method)) {
                ret = delete();
            } else if ("post".equalsIgnoreCase(method)) {
                ret = post();
            } else {
                LogHelper.error(StringUtil.format("不支持的方法请求: URL:{0}, METHOD:{1}, HEADER:{2}, PARAM:{3}", url, method, header, param));
                throw new CommonException("不支持的方法: " + method);
            }
            LogHelper.info(StringUtil.format("URL:{0}, METHOD:{1}, HEADER:{2}, PARAM:{3}, RET:{4}", url, method, header, param, ret));
            return ret;
        })).recover(throwable -> {
            // 回退函数
            LogHelper.error(StringUtil.format("Fallback: [URL]:{0}, [METHOD]:{1}, [PARAM]:{2}, [HEADER]:{3} /r/n {4}",
                    url, method, param, header, throwable.getMessage()));
            LogHelper.error(throwable);

            Map map = new HashMap<>();
            if (throwable instanceof CommonException) {
                CommonException commonEx = (CommonException) throwable;
                map.put("errcode", 500);
                map.put("statetext", "Internal Server Error");
                map.put("msg", commonEx.getMessage());
            } else {
                map.put("errcode", 500);
                map.put("statetext", "Internal Server Error");
                map.put("msg", throwable.getMessage());
            }

            return map;
        });

        return result.get();
    }

    private Map get() {
        HttpHeaders httpHeaders = new HttpHeaders();
        if (header != null && header.size() > 0) {
            for (String key : header.keySet()) {
                httpHeaders.add(key, header.get(key));
            }
        }

        HttpEntity httpEntity = new HttpEntity(httpHeaders);
        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, Map.class);
        if (responseEntity.getStatusCodeValue() != 200) {
            throw new CommonException(JsonConvert.serializeObject(responseEntity.getBody()));
        }
        Map map = responseEntity.getBody();
        return map;
    }

    private Map post() {
        HttpHeaders httpHeaders = new HttpHeaders();
        if (header != null && header.size() > 0) {
            for (String key : header.keySet()) {
                httpHeaders.add(key, header.get(key));
            }
        }
        HttpEntity httpEntity = new HttpEntity(httpHeaders);
        if (param != null && param.size() > 0) {
            httpEntity = new HttpEntity(param, httpHeaders);
        }

        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, Map.class);
        if (responseEntity.getStatusCodeValue() != 200) {
            throw new CommonException(JsonConvert.serializeObject(responseEntity.getBody()));
        }
        Map map = responseEntity.getBody();
        return map;
    }

    private Map patch() {
        HttpHeaders httpHeaders = new HttpHeaders();
        if (header != null && header.size() > 0) {
            for (String key : header.keySet()) {
                httpHeaders.add(key, header.get(key));
            }
        }
        HttpEntity httpEntity = new HttpEntity(httpHeaders);
        if (param != null && param.size() > 0) {
            httpEntity = new HttpEntity(param, httpHeaders);
        }
        RestTemplate client = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
        ResponseEntity<Map> responseEntity = client.exchange(url, HttpMethod.PATCH, httpEntity, Map.class);
        if (responseEntity.getStatusCodeValue() != 200) {
            throw new CommonException(JsonConvert.serializeObject(responseEntity.getBody()));
        }
        Map map = responseEntity.getBody();
        return map;
    }

    private Map put() {
        HttpHeaders httpHeaders = new HttpHeaders();
        if (header != null && header.size() > 0) {
            for (String key : header.keySet()) {
                httpHeaders.add(key, header.get(key));
            }
        }
        HttpEntity httpEntity = new HttpEntity(httpHeaders);
        if (param != null && param.size() > 0) {
            httpEntity = new HttpEntity(param, httpHeaders);
        }

        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.PUT, httpEntity, Map.class);
        if (responseEntity.getStatusCodeValue() != 200) {
            throw new CommonException(JsonConvert.serializeObject(responseEntity.getBody()));
        }
        Map map = responseEntity.getBody();
        return map;
    }

    private Map delete() {
        HttpHeaders httpHeaders = new HttpHeaders();
        if (header != null && header.size() > 0) {
            for (String key : header.keySet()) {
                httpHeaders.add(key, header.get(key));
            }
        }
        HttpEntity httpEntity = new HttpEntity(httpHeaders);
//        if (param != null && param.size() > 0) {
//            httpEntity = new HttpEntity(param, httpHeaders);
//        }

        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.DELETE, httpEntity, Map.class);
        if (responseEntity.getStatusCodeValue() != 200) {
            throw new CommonException(JsonConvert.serializeObject(responseEntity.getBody()));
        }
        Map map = responseEntity.getBody();
        return map;
    }
}

